﻿// <copyright file="HSL.cs" company="BerlinSoft">
// Copyright (c) 2009 All Right Reserved
// </copyright>
// <author>Wernfried Schwenkner</author>
// <email>wernfried@schwenkner.de</email>
// <date>2009-10-21</date>
// <summary>
// Contains the class of HSL.
// </summary>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TimeTracker.Controls.ColorPickerForm.Base
{
	/// <summary>
	/// Structure to define HSL.
	/// </summary>
	public struct HSL
	{
		/// <summary>
		/// Gets an empty HSL structure;
		/// </summary>
		public static readonly HSL Empty = new HSL();

		/// <summary>
		/// field containing the hue value
		/// </summary>
		private int hue;

		/// <summary>
		/// field contains the saturation value
		/// </summary>
		private int saturation;

		/// <summary>
		/// field contains the luminance value
		/// </summary>
		private int luminance;

		/// <summary>
		/// Initializes a new instance of the <see cref="HSL"/> struct.
		/// </summary>
		/// <param name="hue">Hue value.</param>
		/// <param name="saturation">Saturation value.</param>
		/// <param name="luminance">Lightness value.</param>
		public HSL(int hue, int saturation, int luminance)
		{
			this.hue = (hue > 360) ? 360 : ((hue < 0) ? 0 : hue);
			this.saturation = (saturation > 255) ? 255 : ((saturation < 0) ? 0 : saturation);
			this.luminance = (luminance > 255) ? 255 : ((luminance < 0) ? 0 : luminance);
		}

		/// <summary>
		/// Gets or sets the hue component.
		/// </summary>
		public int Hue
		{
			get { return hue; }
			set { hue = (value > 360) ? 360 : ((value < 0) ? 0 : value); }
		}

		/// <summary>
		/// Gets or sets saturation component.
		/// </summary>
		public int Saturation
		{
			get { return saturation; }
			set { saturation = (value > 255) ? 255 : ((value < 0) ? 0 : value); }
		}

		/// <summary>
		/// Gets or sets the luminance component.
		/// </summary>
		public int Luminance
		{
			get { return luminance; }
			set { luminance = (value > 255) ? 255 : ((value < 0) ? 0 : value); }
		}

		/// <summary>
		/// operator == to compare RGB colors
		/// </summary>
		/// <param name="item1">first value to be compared</param>
		/// <param name="item2">second value to be compared</param>
		/// <returns>true, if all components have the same value</returns>
		public static bool operator ==(HSL item1, HSL item2)
		{
			return
				item1.Hue == item2.Hue &&
				item1.Saturation == item2.Saturation &&
				item1.Luminance == item2.Luminance;
		}

		/// <summary>
		/// operator != to compare RGV colors
		/// </summary>
		/// <param name="item1">first value to be compared</param>
		/// <param name="item2">second value to be compared</param>
		/// <returns>true, if one component of both colors differ</returns>
		public static bool operator !=(HSL item1, HSL item2)
		{
			return
				item1.Hue != item2.Hue ||
				item1.Saturation != item2.Saturation ||
				item1.Luminance != item2.Luminance;
		}

		/// <summary>
		/// Converts HSL to RGB.
		/// </summary>
		/// <param name="hue">Hue, must be in [0, 360].</param>
		/// <param name="saturation">Saturation, must be in [0, 255].</param>
		/// <param name="luminance">Luminance, must be in [0, 255].</param>
		/// <returns>The RGB value of the color</returns>
		public static RGB ToRGB(int hue, int saturation, int luminance)
		{
			if (saturation == 0)
			{
				// achromatic color (gray scale)
				return new RGB(luminance, luminance, luminance);
			}
			else
			{
				double luminanceNormed = (double)luminance / 255;
				double saturationNormed = (double)saturation / 255;
				double q = (luminanceNormed < 0.5) ? (luminanceNormed * (1.0 + saturationNormed)) : (luminanceNormed + saturationNormed - (luminanceNormed * saturationNormed));
				double p = (2.0 * luminanceNormed) - q;

				int q_ = (luminance < 128) ? (luminance * (255 + saturation) / 256) : (luminance + saturation - (luminance * saturation) / 256);
				int p_ = (2 * luminance) - q_;

				double hueNormed = hue / 360.0;
				double[] t = new double[3];
				t[0] = hueNormed + (1.0 / 3.0);			// Tr
				t[1] = hueNormed;										// Tb
				t[2] = hueNormed - (1.0 / 3.0);			// Tg

				int[] t_ = new int[3];

				t_[0] = hue + 120;
				t_[1] = hue;
				t_[2] = hue - 120;

				for (int i = 0; i < 3; i++)
				{
					if (t[i] < 0)
					{
						t[i] += 1.0;
					}

					if (t_[i] < 0)
					{
						t_[i] += 360;
					}

					if (t[i] > 1)
					{
						t[i] -= 1.0;
					}

					if (t_[i] > 360)
					{
						t_[i] -= 360;
					}

					if ((t[i] * 6) < 1)
					{
						t[i] = p + ((q - p) * 6.0 * t[i]);
					}
					else if ((t[i] * 2.0) < 1)
					{ // (1.0/6.0)<=T[i] && T[i]<0.5
						t[i] = q;
					}
					else if ((t[i] * 3.0) < 2)
					{ // 0.5<=T[i] && T[i]<(2.0/3.0)
						t[i] = p + (q - p) * ((2.0 / 3.0) - t[i]) * 6.0;
					}
					else
					{
						t[i] = p;
					}

					// ----------------------
					if (t_[i] < 60)
					{
						t_[i] = p_ + (q_ - p_) * t_[i] / 60;
					}
					else if (t_[i] < 180)
					{ // (1.0/6.0)<=T[i] && T[i]<0.5
						t_[i] = q_;
					}
					else if (t_[i] < 240)
					{ // 0.5<=T[i] && T[i]<(2.0/3.0)
						t_[i] = p_ + (q_ - p_) * (240 - t_[i]) / 60;
					}
					else
					{
						t_[i] = p_;
					}
				}

				return new RGB(t_[0], t_[1], t_[2]);
			}
		}

		/// <summary>
		/// Compares if two instances of the class are equal
		/// </summary>
		/// <param name="other">the other instance to compare</param>
		/// <returns>true, if both instances have the same color</returns>
		public override bool Equals(object other)
		{
			if (other == null || GetType() != other.GetType())
			{
				return false;
			}

			return this == (HSL)other;
		}

		/// <summary>
		/// Calculates the hash code
		/// </summary>
		/// <returns>The hash code of the color</returns>
		public override int GetHashCode()
		{
			return Hue.GetHashCode() ^ Saturation.GetHashCode() ^ Luminance.GetHashCode();
		}

		/// <summary>
		/// Converts an HSL color to RGB
		/// </summary>
		/// <returns>The RGB value.</returns>
		public RGB ToRGB()
		{
			return ToRGB(hue, saturation, luminance);
		}
	}
}
